library ieee;
use IEEE.STD_LOGIC_TEXTIO.all;
use STD.TEXTIO.all;
use ieee.std_logic_1164.all;


entity tb_icache is
	generic (Period : Time := 100 ns;
           Debug : Boolean := False);
end tb_icache;

architecture testbench_arch of tb_icache is
	
	-- component(s)
  component icache port (
		clk, nrst : in  std_logic;
		tag_in    : in  std_logic_vector(25 downto 0);
		data_in   : in  std_logic_vector(31 downto 0);
		data_out  : out std_logic_vector(31 downto 0);
		index_in  : in  std_logic_vector(3 downto 0);
		hit       : out std_logic;
		icache_en : in  std_logic);
  end component;
	
	-- signals
  signal clk, nReset, done	: std_logic;
  signal tag_in : std_logic_vector(25 downto 0);
  signal data_in, data_out : std_logic_vector(31 downto 0);
  signal index_in : std_logic_vector(3 downto 0);
  signal hit, icache_en : std_logic;
  signal address_in : std_logic_vector(31 downto 0);
	
	-- constants
  constant zero_v : std_logic_vector := x"00000000";
	
	-- print procedures
  procedure println( output_string : in string ) is
    variable lout                  :    line;
  begin
    WRITE(lout, output_string);
    WRITELINE(OUTPUT, lout);
  end println;

  procedure printlv( output_bv : in std_logic_vector ) is
    variable lout              :    line;
  begin
    WRITE(lout, output_bv);
    WRITELINE(OUTPUT, lout);
  end printlv;


begin

	-- DUT
  DUT : icache
  	port map (
			clk       => clk,
			nrst      => nReset,
			tag_in    => tag_in,
			data_in   => data_in,
			data_out  => data_out,
			index_in  => index_in,
			hit       => hit,
			icache_en => icache_en
		);

	-- generate clock signal
  clkgen: process
    variable clk_tmp : std_logic := '0';
  begin
    clk_tmp := not clk_tmp;
    clk <= clk_tmp;
    wait for Period/2;
  end process;

  tag_in <= address_in(31 downto 6);
  index_in <= address_in(5 downto 2);
	
	-- TESTER
  process
  begin
  	done <= '0';
    println("");
    println("Starting Test");
    
    -- reset
    nReset <= '0';
    wait for Period/2;
    wait for 1 ns;
    address_in <= x"00000000";
    data_in <= x"00000000";
    icache_en <= '0';
    nReset <= '1';
    
    
    wait for Period/2;

		report "CPU requests cache data; misses";
		assert hit='0' report "should be a miss!";
		
    wait for Period/2;

    report "icache controller enters miss state; hooks icache to mem";
    address_in <= x"00000000";
    data_in <= x"DEADBEEF";
    icache_en <= '1';

    wait for Period/2;

    report "memory gets locked in to cache";

    wait for Period/2;

		report "icache controller leaves miss state; hooks icache to cpu";
    icache_en <= '0';

    wait for Period/2;

		report "CPU wants next data; misses";
    address_in <= x"00000004";

    wait for Period/2;
    assert hit='0' report "should be a miss!";

		report "icache controller enters miss state; hooks icache to mem";
    address_in <= x"00000004";
    data_in <= x"BEEFDEAD";
    icache_en <= '1';

    wait for Period/2;

		report "memory gets locked in to cache";
		
    wait for Period/2;

		report "icache controller leaves miss state; hooks icache up to cpu";
    icache_en <= '0';

    wait for Period/2;

		report "CPU requests earlier data (like a jump instruction might); hit";
    address_in <= x"00000000";
    assert hit='1' report "Should be a hit!";
    

    println("Test Complete");
    println("");
    -- end simulation
    done <= '1';
    wait;
  end process;
  
end testbench_arch;


